package com.example.reactive.config;

import com.example.reactive.vo.UserVO;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

import java.util.function.Function;

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfig {

    @Bean
    SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        // @formatter:off
        http
                .authorizeExchange((authorize) -> authorize
                        .pathMatchers("/login").permitAll()
                        .pathMatchers("/admin/**").hasRole("ADMIN")
                        .anyExchange().authenticated()
                )
                .httpBasic(Customizer.withDefaults())
                .formLogin(Customizer.withDefaults());
        // @formatter:on
        return http.build();
    }

    @Bean
    MapReactiveUserDetailsService userDetailsService() {
        User.UserBuilder userBuilder = User.builder().passwordEncoder(passwordEncoder()::encode);
        UserDetails user = userBuilder
                .username("user")
                .password("123456")
                .roles("USER")
                .build();
        UserDetails admin = userBuilder
                .username("admin")
                .password("admin")
                .roles("USER","ADMIN")
                .build();
        return new MapReactiveUserDetailsService(user, admin);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    // 自定义 @PreAuthorize 方法认证处理规则
    @Bean
    public Function<UserVO, Mono<Boolean>> func() {
        return (userVO) -> Mono.defer(() -> Mono.just(userVO.getName().equals("user") && userVO.getPassword().equals(12)));
    }
}


// 关于如何在 Reactive Web 应用中使用 Spring Security 的参考文档： https://docs.springjava.cn/spring-security/reference/reactive/getting-started.html

// Spring WebFlux 包含一个用于执行 HTTP 请求的客户端。WebClient 具有基于 Reactor 的功能性流畅 API（请参阅 “Reactive Libraries”），
// 可以声明式地组成异步逻辑，而无需处理线程或并发问题。它是完全无阻塞的，支持流式传输，并依赖于同样的编解码器，这些编解码器也用于对服务器端的请求和响应内容进行编码和解码。